﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace UnitTesting.Fixturing
{
	class DelegateValueProvider : IValueProvider
	{
		readonly IValueProvider _valueProvider;
		MethodInfo _delegateMethod;

		internal DelegateValueProvider(IValueProvider valueProvider)
		{
			_valueProvider = valueProvider;
		}

		public object Create(Type type)
		{
			Ensure.IsNotNull(() => type);
			if (typeof (Delegate).IsAssignableFrom(type))
			{
				_delegateMethod = type.GetMethod("Invoke");
				return Expression.Lambda(type, CreateBody(), CreateParameters()).Compile();
			}
			return NotResolved.Value;
		}

		IEnumerable<ParameterExpression> CreateParameters()
		{
			int paramCount = 0;
			var parameters = _delegateMethod.GetParameters()
				.Select(param => Expression.Parameter(param.ParameterType, String.Concat("arg", paramCount++)));
			return parameters;
		}

		Expression CreateBody()
		{
			var returnType = _delegateMethod.ReturnType;
			Expression body;
			if (returnType == typeof (void))
			{
				body = Expression.Constant(null);
			}
			else
			{
				var returnValue = _valueProvider.Create(returnType);
				body = Expression.Convert(Expression.Constant(returnValue), returnType);
			}
			return body;
		}
	}
}